package com.uama.plugin.service;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.util.Processor;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class MyJavaService {
    private Project project;
    private JavaPsiFacade javaPsiFacade;
    private Map<String, PsiClass[]> providerCache = Maps.newHashMap();
    private Map<String, PsiClass[]> serviceCache = Maps.newHashMap();

    public MyJavaService(Project project) {
        this.project = project;
        this.javaPsiFacade = JavaPsiFacade.getInstance(project);
    }

    public static MyJavaService getInstance(@NotNull Project project) {
        return ServiceManager.getService(project, MyJavaService.class);
    }

    public void process(PsiMethod psiMethod, Processor<? super PsiMethod> processor) {
        PsiClass psiClass = psiMethod.getContainingClass();
        if (null == psiClass) return;
        PsiAnnotation psiAnnotation = psiClass.getAnnotation("org.springframework.cloud.openfeign.FeignClient");
        if (psiAnnotation == null) {
            psiAnnotation = psiClass.getAnnotation("org.springframework.cloud.netflix.feign.FeignClient");
        }
        if (psiAnnotation != null) {
            processService(psiMethod, processor);
        } else {
            processProvider(psiMethod, processor);
        }
    }

    private void processService(PsiMethod psiMethod, Processor<? super PsiMethod> processor) {
        PsiClass psiClass = psiMethod.getContainingClass();
        if (null == psiClass) return;
        Project project = psiMethod.getProject();
        String className = psiClass.getName();
        if (StringUtils.isBlank(className)) {
            return;
        }
        String newClassName = "";
        if (className.endsWith("ServiceV1")) {
            newClassName = className.substring(0, className.indexOf("ServiceV1"));
        } else if (className.endsWith("ServiceV2")) {
            newClassName = className.substring(0, className.indexOf("ServiceV2"));

            //兼容life微服务，life微服务有些是V1结尾 有些是Service结尾
        } else if (className.endsWith("V1")) {
            newClassName = className.substring(0, className.indexOf("V1"));
        } else if (className.endsWith("V2")) {
            newClassName = className.substring(0, className.indexOf("V2"));
        } else if (className.endsWith("Service")) {
            newClassName = className.substring(0, className.indexOf("Service"));
        }

        // 兼容butler微服务，butler微服务都是 VisitorProviderServiceV1 这么写的，不需要再拼上Provider
        if (!newClassName.endsWith("Provider")) {
            newClassName = newClassName + "Provider";
        }

        GlobalSearchScope searchScope = getSearchScopeByModuleName(psiMethod, "api", "provider");

        PsiClass[] providerNames = providerCache.computeIfAbsent(newClassName, k -> PsiShortNamesCache.getInstance(project)
                .getClassesByName(k, searchScope));

        buildProcessor(psiMethod, providerNames, processor);

    }

    private void processProvider(PsiMethod psiMethod, Processor<? super PsiMethod> processor) {
        PsiClass psiClass = psiMethod.getContainingClass();
        if (null == psiClass) return;

        String className = psiClass.getName();
        if (StringUtils.isBlank(className)) {
            return;
        }
        if (className.endsWith("Provider")) {
            PsiClass[] psiClasses = serviceCache.get(className);
            if (psiClasses != null) {
                buildProcessor(psiMethod, psiClasses, processor);
                return;
            }

            String newClassName;
            String shortClassName = className.substring(0, className.indexOf("Provider"));

            GlobalSearchScope searchScope = getSearchScopeByModuleName(psiMethod, "provider", "api");
            newClassName = shortClassName + "ServiceV1";
            List<PsiClass> all = new ArrayList<>(8);
            PsiClass[] classesByName = PsiShortNamesCache.getInstance(project).getClassesByName(newClassName, searchScope);
            all.addAll(Lists.newArrayList(classesByName));
            if (all.size() < 2) {
                newClassName = shortClassName + "ServiceV2";
                classesByName = PsiShortNamesCache.getInstance(project).getClassesByName(newClassName, searchScope);
                all.addAll(Lists.newArrayList(classesByName));
            }
            // 为了支持 butler、life 中一些不规范的命名
            if (all.size() < 2) {
                newClassName = className + "ServiceV1";
                classesByName = PsiShortNamesCache.getInstance(project).getClassesByName(newClassName, searchScope);
                all.addAll(Lists.newArrayList(classesByName));
            }
            if (all.size() < 2) {
                newClassName = className + "ServiceV2";
                classesByName = PsiShortNamesCache.getInstance(project).getClassesByName(newClassName, searchScope);
                all.addAll(Lists.newArrayList(classesByName));
            }
            if (all.size() < 2) {
                newClassName = shortClassName + "V1";
                classesByName = PsiShortNamesCache.getInstance(project).getClassesByName(newClassName, searchScope);
                all.addAll(Lists.newArrayList(classesByName));
            }
            if (all.size() < 2) {
                newClassName = shortClassName + "V2";
                classesByName = PsiShortNamesCache.getInstance(project).getClassesByName(newClassName, searchScope);
                all.addAll(Lists.newArrayList(classesByName));
            }
            if (all.size() < 2) {
                newClassName = shortClassName + "Service";
                classesByName = PsiShortNamesCache.getInstance(project).getClassesByName(newClassName, searchScope);
                all.addAll(Lists.newArrayList(classesByName));
            }

            PsiClass[] allArray = all.toArray(new PsiClass[0]);
            serviceCache.put(className, allArray);
            buildProcessor(psiMethod, allArray, processor);
        }
    }

    private void buildProcessor(PsiMethod psiMethod, PsiClass[] psiClasses, Processor<? super PsiMethod> processor) {
        for (PsiClass psiClazz : psiClasses) {
            PsiMethod[] methods = psiClazz.getMethods();
            for (PsiMethod m : methods) {
                if (m.getName().equals((psiMethod.getName()))) {
                    processor.process(m);
                }
            }
        }
    }

    private GlobalSearchScope getSearchScopeByModuleName(PsiElement psiElement, String replaceSource, String replaceTarget) {
        Module module = ModuleUtil.findModuleForPsiElement(psiElement);
        if (module != null) {
            String moduleName = module.getName().replace(replaceSource, replaceTarget);
            Module moduleByName = ModuleManager.getInstance(project).findModuleByName(moduleName);
            if (moduleByName != null) {
                return GlobalSearchScope.moduleScope(moduleByName);
            }
        }
        return GlobalSearchScope.allScope(project);
    }

    public static void main(String[] args) {
        List<String> str = new ArrayList<>();
        str.add("456");
        str.add("123");
        String[] strings = str.toArray(new String[0]);
        for (String s : strings) {
            System.out.println(s);
        }
    }
}
